-module(rtps_qos).

%%% @doc This module encodes and decodes DDS QoS to / from parameters
%%% which can be handled by RTPS. This module is a reminder how to do
%%% this and should be part of DDS.

-include_lib("eunit/include/eunit.hrl").
-include("rtps.hrl").
-include("rtps_qos.hrl").

-export([enc_qos/1, dec_qos/2, get_qos/2, get_qos/3]).

-ifndef(ENDIANNESS).
  -define(ENDIANNESS, big). % or little
-endif.

-export_type([qos/0, qos_id/0]).
-type qos() :: #user_data_qos{} | #durability_qos{} | #presentation_qos{} | #deadline_qos{} |
	       #latency_budget_qos{} | #ownership_qos{} | #ownership_strength_qos{} |
	       #liveliness_qos{} | #time_based_filter_qos{} | #partition_qos{} |
	       #reliability_qos{} | #destination_order_qos{} | #history_qos{} |
	       #resource_limits_qos{} | #entity_factory_qos{} | #writerdatalifecycle_qos{} |
	       #readerdatalifecycle_qos{} | #topic_data_qos{} | #group_data_qos{} |
	       #transport_priority_qos{} | #lifespan_qos{} | #durability_service_qos{}.
-type qos_id() :: user_data_qos | durability_qos | presentation_qos | deadline_qos |
		  latency_budget_qos | ownership_qos | ownership_strength_qos |
		  liveliness_qos | time_based_filter_qos | partition_qos |
		  reliability_qos | destination_order_qos | history_qos |
		  resource_limits_qos | entity_factory_qos | writerdatalifecycle_qos |
		  readerdatalifecycle_qos | topic_data_qos | group_data_qos |
		  transport_priority_qos | lifespan_qos | durability_service_qos.

-spec get_qos(Qos_id, List) -> Qos | undefined when
      Qos_id:: qos_id(),
      List :: [Qos],
      Qos :: qos().
get_qos(Tag, List) ->
    get_qos(Tag, List, undefined).

-spec get_qos(Qos_id, List, Default) -> Qos | Default when
      Qos_id:: qos_id(),
      List :: [Qos],
      Default :: qos() | term(),
      Qos :: qos().
get_qos(Tag, List, Default) ->
    case lists:keyfind(Tag, 1, List) of
	false ->
	    Default;
	Qos ->
	    Qos
    end.

-spec enc_qos(Qos) -> #parameter{id :: Id, value :: Bin} when
      Qos :: qos(),
      Id :: rtps_qos:qos_id(),
      Bin :: binary().
enc_qos(#deadline_qos{period = Period}) ->
    Bin = rtps_psm:enc_duration(Period),
    #parameter{id = deadline_qos, value = Bin};
enc_qos(#destination_order_qos{kind = Kind}) ->
    Bin = rtps_psm:enc_enum(Kind, ?DESTINATION_ORDER_QOS_KIND),
    #parameter{id = destination_order_qos, value = Bin};
enc_qos(#durability_qos{kind = Kind}) ->
    Bin = rtps_psm:enc_enum(Kind, ?DURABILITY_QOS_KIND),
    #parameter{id = durability_qos, value = Bin};
enc_qos(#durability_service_qos{service_cleanup_delay = Service_cleanup_delay,
				history_kind  = History_kind,
				history_depth = History_depth,
				max_samples = Max_samples,
				max_instances = Max_instances,
				max_samples_per_instance = Max_samples_per_instance}) ->
    Bin1 = rtps_psm:enc_duration(Service_cleanup_delay),
    Bin2 = rtps_psm:enc_enum(History_kind, ?HISTORY_QOS_KIND, Bin1),
    Bin3 = rtps_psm:enc_long(to_n(History_depth), Bin2),
    Bin4 = rtps_psm:enc_long(to_n(Max_samples), Bin3),
    Bin5 = rtps_psm:enc_long(to_n(Max_instances), Bin4),
    Bin6 = rtps_psm:enc_long(to_n(Max_samples_per_instance), Bin5),
    #parameter{id = durability_service_qos, value = Bin6};
enc_qos(#entity_factory_qos{autoenable_created_entities = Autoenable_created_entities}) ->
    Bin = rtps_psm:enc_bool(Autoenable_created_entities),
    #parameter{id = entity_factory_qos, value = Bin};
enc_qos(#group_data_qos{value = Value})
  when is_binary(Value) ->
    Bin = rtps_psm:enc_sequence(Value),
    #parameter{id = group_data_qos, value = Bin};
enc_qos(#history_qos{kind = Kind, depth = Depth}) ->
    Bin1 = rtps_psm:enc_enum(Kind, ?HISTORY_QOS_KIND),
    Bin2 = rtps_psm:enc_long(to_n(Depth), Bin1),
    #parameter{id = history_qos, value = Bin2};
enc_qos(#latency_budget_qos{duration = Duration}) ->
    Bin = rtps_psm:enc_duration(Duration),
    #parameter{id = latency_budget_qos, value = Bin};
enc_qos(#lifespan_qos{duration = Duration}) ->
    Bin = rtps_psm:enc_duration(Duration),
    #parameter{id = lifespan_qos, value = Bin};
enc_qos(#liveliness_qos{kind = Kind, lease_duration = Lease_duration}) ->
    Bin1 = rtps_psm:enc_enum(Kind, ?LIVELINESS_QOS_KIND), 
    Bin2 = rtps_psm:enc_duration(Lease_duration, Bin1),
    #parameter{id = liveliness_qos, value = Bin2};
enc_qos(#ownership_qos{kind = Kind}) ->
    Bin = rtps_psm:enc_enum(Kind, ?OWNERSHIP_QOS_KIND),
    #parameter{id = ownership_qos, value = Bin};
enc_qos(#ownership_strength_qos{value = Value}) ->
    Bin = rtps_psm:enc_long(Value),
    #parameter{id = ownership_strength_qos, value = Bin};
enc_qos(#partition_qos{name = Names}) ->
    Bin = rtps_psm:enc_sequence(Names, fun rtps_psm:enc_string/2),
    #parameter{id = partition_qos, value = Bin};
enc_qos(#presentation_qos{access_scope = Access_scope,
			  coherent_access = Coherent_access,
			  ordered_access = Ordered_access}) ->

    Bin1 = rtps_psm:enc_enum(Access_scope, ?PRESENTATION_QOS_ACCESS_SCOPE), 
    Bin2 = rtps_psm:enc_bool(Coherent_access, Bin1),
    Bin3 = rtps_psm:enc_bool(Ordered_access, Bin2),
    #parameter{id = presentation_qos, value = Bin3};
enc_qos(#readerdatalifecycle_qos{autopurge_nowriter_samples_delay = Autopurge_nowriter_samples_delay,
				 autopurge_disposed_samples_delay = Autopurge_disposed_samples_delay }) ->
    Bin1 = rtps_psm:enc_duration(Autopurge_nowriter_samples_delay),
    Bin2 = rtps_psm:enc_duration(Autopurge_disposed_samples_delay, Bin1),
    #parameter{id = readerdatalifecycle_qos, value = Bin2};
enc_qos(#reliability_qos{kind = Kind, max_blocking_time = Max_blocking_time}) ->
    Bin1 = rtps_psm:enc_enum(Kind, ?RELIABILITY_QOS_KIND), 
    Bin2 = rtps_psm:enc_duration(Max_blocking_time, Bin1),
    #parameter{id = reliability_qos, value = Bin2};
enc_qos(#resource_limits_qos{max_samples = Max_samples,
			     max_instances = Max_instances,
			     max_samples_per_instance = Max_samples_per_instance}) ->
    Bin1 = rtps_psm:enc_long(to_n(Max_samples)),
    Bin2 = rtps_psm:enc_long(to_n(Max_instances), Bin1),
    Bin3 = rtps_psm:enc_long(to_n(Max_samples_per_instance), Bin2),
    #parameter{id = resource_limits_qos, value = Bin3};
enc_qos(#time_based_filter_qos{minimum_separation = Minimum_separation}) ->
    Bin = rtps_psm:enc_duration(Minimum_separation),
    #parameter{id = time_based_filter_qos, value = Bin};
enc_qos(#topic_data_qos{value = Value})
  when is_binary(Value) ->
    Bin = rtps_psm:enc_sequence(Value),
    #parameter{id = topic_data_qos, value = Bin};
enc_qos(#transport_priority_qos{value = Value})
  when is_integer(Value) ->
    Bin = rtps_psm:enc_long(Value),
    #parameter{id = transport_priority_qos, value = Bin};
enc_qos(#user_data_qos{value = Value})
  when is_binary(Value) ->
    Bin = rtps_psm:enc_sequence(Value),
    #parameter{id = user_data_qos, value = Bin};
enc_qos(#writerdatalifecycle_qos{autodispose_unregistered_instances = Autodispose_unregistered_instances}) ->
    Bin = rtps_psm:enc_bool(Autodispose_unregistered_instances),
    #parameter{id = writerdatalifecycle_qos, value = Bin}.


-spec dec_qos(Endianness, #parameter{id :: Id, value :: Bin}) -> Qos | invalid_qos when
      Endianness :: big | little,
      Id :: 1..22,
      Bin :: binary(),
      Qos :: qos().
dec_qos(Endianness, #parameter{id = ?DEADLINE_QOS_ID, value = Bin}) ->
    {Period, _} = rtps_psm:dec_duration(Endianness, Bin),
    #deadline_qos{period = Period};
dec_qos(Endianness, #parameter{id = ?DESTINATION_ORDER_QOS_ID, value = Bin}) ->
    {Kind, _} = rtps_psm:dec_enum(Endianness, ?DESTINATION_ORDER_QOS_KIND, Bin),
    #destination_order_qos{kind = Kind};
dec_qos(Endianness, #parameter{id = ?DURABILITY_QOS_ID, value = Bin}) ->
    {Kind, _} = rtps_psm:dec_enum(Endianness, ?DURABILITY_QOS_KIND, Bin),
    #durability_qos{kind = Kind};
dec_qos(Endianness, #parameter{id = ?DURABILITY_SERVICE_QOS_ID, value = Bin}) ->
    {Service_cleanup_delay, Bin1} = rtps_psm:dec_duration(Endianness, Bin),
    {History_kind, Bin2} = rtps_psm:dec_enum(Endianness, ?HISTORY_QOS_KIND, Bin1),
    {History_depth, Bin3} = rtps_psm:dec_long(Endianness, Bin2),
    {Max_samples, Bin4} = rtps_psm:dec_long(Endianness, Bin3),
    {Max_instances, Bin5} = rtps_psm:dec_long(Endianness, Bin4),
    {Max_samples_per_instance, _} = rtps_psm:dec_long(Endianness, Bin5),
    #durability_service_qos{service_cleanup_delay = Service_cleanup_delay,
			    history_kind  = History_kind,
			    history_depth = from_n(History_depth),
			    max_samples = from_n(Max_samples),
			    max_instances = from_n(Max_instances),
			    max_samples_per_instance = from_n(Max_samples_per_instance)};
dec_qos(_Endianness, #parameter{id = ?ENTITY_FACTORY_QOS_ID, value = Bin}) ->
    {Autoenable_created_entities, _} = rtps_psm:dec_bool(Bin),
    #entity_factory_qos{autoenable_created_entities = Autoenable_created_entities};
dec_qos(Endianness, #parameter{id = ?GROUP_DATA_QOS_ID, value = Bin}) ->
    {Value, _} = rtps_psm:dec_sequence(Endianness, Bin),
    #group_data_qos{value = Value};
dec_qos(Endianness, #parameter{id = ?HISTORY_QOS_ID, value = Bin}) ->
    {Kind, Bin1} = rtps_psm:dec_enum(Endianness, ?HISTORY_QOS_KIND, Bin),
    {Depth, _} = rtps_psm:dec_long(Endianness, Bin1),
    #history_qos{kind = Kind, depth = from_n(Depth)};
dec_qos(Endianness, #parameter{id = ?LATENCY_BUDGET_QOS_ID, value = Bin}) ->
    {Duration, _} = rtps_psm:dec_duration(Endianness, Bin),
    #latency_budget_qos{duration = Duration};
dec_qos(Endianness, #parameter{id = ?LIFESPAN_QOS_ID, value = Bin}) ->
    {Duration, _} = rtps_psm:dec_duration(Endianness, Bin),
    #lifespan_qos{duration = Duration};
dec_qos(Endianness, #parameter{id = ?LIVELINESS_QOS_ID, value = Bin}) ->
    {Kind, Bin1} = rtps_psm:dec_enum(Endianness, ?LIVELINESS_QOS_KIND, Bin), 
    {Lease_duration, _} = rtps_psm:dec_duration(Endianness, Bin1),
    #liveliness_qos{kind = Kind, lease_duration = Lease_duration};
dec_qos(Endianness, #parameter{id = ?OWNERSHIP_QOS_ID, value = Bin}) ->
    {Kind, _} = rtps_psm:dec_enum(Endianness, ?OWNERSHIP_QOS_KIND, Bin),
    #ownership_qos{kind = Kind};
dec_qos(Endianness, #parameter{id = ?OWNERSHIP_STRENGTH_QOS_ID, value = Bin}) ->
    {Value, _} = rtps_psm:dec_long(Endianness, Bin),
    #ownership_strength_qos{value = Value};
dec_qos(Endianness, #parameter{id = ?PARTITION_QOS_ID, value = Bin}) ->
    {Names, _} = rtps_psm:dec_sequence(Endianness, fun rtps_psm:dec_string/2, Bin),
    #partition_qos{name = Names};
dec_qos(Endianness, #parameter{id = ?PRESENTATION_QOS_ID, value = Bin}) ->
    {Access_scope, Bin1} = rtps_psm:dec_enum(Endianness, ?PRESENTATION_QOS_ACCESS_SCOPE, Bin),
    {Coherent_access, Bin2} = rtps_psm:dec_bool(Bin1),
    {Ordered_access, _} = rtps_psm:dec_bool(Bin2),
    #presentation_qos{access_scope = Access_scope,
		      coherent_access = Coherent_access,
		      ordered_access = Ordered_access};
dec_qos(Endianness, #parameter{id = ?READERDATALIFECYCLE_QOS_ID, value = Bin}) ->
    {Autopurge_nowriter_samples_delay, Bin1} = rtps_psm:dec_duration(Endianness, Bin),
    {Autopurge_disposed_samples_delay, _} = rtps_psm:dec_duration(Endianness, Bin1),
    #readerdatalifecycle_qos{autopurge_nowriter_samples_delay = Autopurge_nowriter_samples_delay,
			     autopurge_disposed_samples_delay = Autopurge_disposed_samples_delay };
dec_qos(Endianness, #parameter{id = ?RELIABILITY_QOS_ID, value = Bin}) ->
    {Kind, Bin1} = rtps_psm:dec_enum(Endianness, ?RELIABILITY_QOS_KIND, Bin), 
    {Max_blocking_time, _} = rtps_psm:dec_duration(Endianness, Bin1),
    #reliability_qos{kind = Kind, max_blocking_time = Max_blocking_time};
dec_qos(Endianness, #parameter{id = ?RESOURCE_LIMITS_QOS_ID, value = Bin}) ->
    {Max_samples, Bin1} = rtps_psm:dec_long(Endianness, Bin),
    {Max_instances, Bin2} = rtps_psm:dec_long(Endianness, Bin1),
    {Max_samples_per_instance, _} = rtps_psm:dec_long(Endianness, Bin2),
    #resource_limits_qos{max_samples = from_n(Max_samples),
			 max_instances = from_n(Max_instances),
			 max_samples_per_instance = from_n(Max_samples_per_instance)};
dec_qos(Endianness, #parameter{id = ?TIME_BASED_FILTER_QOS_ID, value = Bin}) ->
    {Minimum_separation, _} = rtps_psm:dec_duration(Endianness, Bin),
    #time_based_filter_qos{minimum_separation = Minimum_separation};
dec_qos(Endianness, #parameter{id = ?TOPIC_DATA_QOS_ID, value = Bin}) ->
    {Value, _} = rtps_psm:dec_sequence(Endianness, Bin),
    #topic_data_qos{value = Value};
dec_qos(Endianness, #parameter{id = ?TRANSPORT_PRIORITY_QOS_ID, value = Bin}) ->
    {Value, _} = rtps_psm:dec_long(Endianness, Bin),
    #transport_priority_qos{value = Value};
dec_qos(Endianness, #parameter{id = ?USER_DATA_QOS_ID, value = Bin}) ->
    {Value, _} = rtps_psm:dec_sequence(Endianness, Bin),
    #user_data_qos{value = Value};
dec_qos(_Endianness, #parameter{id = ?WRITERDATALIFECYCLE_QOS_ID, value = Bin}) ->
    {Autodispose_unregistered_instances, _} = rtps_psm:dec_bool(Bin),
    #writerdatalifecycle_qos{autodispose_unregistered_instances = Autodispose_unregistered_instances};
dec_qos(_Endianness, #parameter{id = _Id, value = _Bin}) ->
    invalid_qos.


to_n(length_unlimited) ->
    -1;
to_n(N) when N >= 0 ->
    N.

from_n(-1) ->
    length_unlimited;
from_n(N) when N >= 0 ->
    N.

%%%===================================================================
%%% Tests
%%%===================================================================

-ifdef(EUNIT).

-endif.
